﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using System.IO.Ports;
using Microsoft.Lync.Model;
namespace Yaam
{
    /// <summary>
    /// Encoding for how the LEDs should behave.
    /// </summary>
    public enum LEDEffect : byte
    {
        Solid = 0x04,
        Throb = 0x08,
        Blink = 0x10,
        SOS = 0xF0
    }

    public enum Microcontroller
    {
        Arduino,
        Netduino
    }

    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class YaamWindow : Window
    {
        private LyncClient _lyncClient;
        private Storyboard mySb = new Storyboard();
        private SerialPort _serialPort;
        private int _comPort;
        private int _numLEDs = 1;
        private Microcontroller _microcontroller;
        private LEDEffect _effect = LEDEffect.Throb;
        private IconBitmapDecoder _icon;
        private Dictionary<ContactAvailability, Color> _availabilityColor = new Dictionary<ContactAvailability, Color>();

        internal delegate void GenericDelegate();
        internal delegate void ColorStatusDelegate(System.Windows.Media.Color color);

        internal void InvokeColorStatusDelegate(System.Windows.Media.Color color)
        {
            RadialGradientBrush rb;
            string serialBuffer = "#" + color.ToString().Substring(3);
            rb = (RadialGradientBrush)statusCircle.Fill;
            rb.GradientStops.RemoveAt(1);
            rb.GradientStops.Add(new GradientStop(color, 1));
            statusCircle.Fill = rb;
            if (_microcontroller == Microcontroller.Arduino)
            {
                if (_effect == LEDEffect.Throb)
                {
                    serialBuffer += "01";
                }
                else
                {
                    serialBuffer += "00";
                }
            }
            else
            {
                int ledEffect = (int)_effect;
                ledEffect += _numLEDs;

                serialBuffer += String.Format("{0:X2}", ledEffect);
            }

            if (_effect == LEDEffect.Throb)
            {
                mySb.Begin(statusCircle);
            }

            _serialPort.WriteLine(serialBuffer);
        }

        public YaamWindow()
        {
            InitializeComponent();

            string effect = String.Empty;
            int numLEDs = 1;
            Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            AppSettingsSection appSettings = config.AppSettings;

            if (appSettings.Settings["Effect"] != null)
            {
                effect = appSettings.Settings["Effect"].Value.ToLower();
                switch (effect)
                {
                    case "solid":
                        cbThrob.IsChecked = false;
                        break;

                    case "throb":
                        cbThrob.IsChecked = true;
                        break;

                    default:
                        cbThrob.IsChecked = true;
                        break;
                }
            }
            else
            {
                cbThrob.IsChecked = true;
            }

            if (appSettings.Settings["COMPort"] != null)
            {
                if (!Int32.TryParse(appSettings.Settings["COMPort"].Value, out _comPort))
                {
                    // TODO: Handle this situation better
                    _comPort = 5;
                }
            }

            if (appSettings.Settings["NumLEDs"] != null)
            {
                if (Int32.TryParse(appSettings.Settings["NumLEDs"].Value, out numLEDs))
                {
                    if (_numLEDs > 0 && _numLEDs < 3)
                    {
                        _numLEDs = numLEDs;
                    }
                }
            }

            this.Loaded += new RoutedEventHandler(OnLoaded);
            this.Unloaded += new RoutedEventHandler(OnUnloaded);
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            //
            // Initialize the availability colors
            //
            _availabilityColor.Add(ContactAvailability.Free, Colors.LimeGreen);
            _availabilityColor.Add(ContactAvailability.FreeIdle, Colors.LimeGreen);
            _availabilityColor.Add(ContactAvailability.Busy, Colors.Red);
            _availabilityColor.Add(ContactAvailability.BusyIdle, Colors.Red);
            _availabilityColor.Add(ContactAvailability.DoNotDisturb, Colors.DarkRed);
            _availabilityColor.Add(ContactAvailability.Away, Colors.Yellow);
            _availabilityColor.Add(ContactAvailability.TemporarilyAway, Colors.Yellow);
            _availabilityColor.Add(ContactAvailability.Offline, Colors.LightSlateGray);

            //Listen for events of changes in the state of the client
            try
            {
                _lyncClient = LyncClient.GetClient();
            }
            catch (ClientNotFoundException clientNotFoundException)
            {
                Console.WriteLine(clientNotFoundException);
                return;
            }
            catch (NotStartedByUserException notStartedByUserException)
            {
                Console.Out.WriteLine(notStartedByUserException);
                return;
            }
            catch (LyncClientException lyncClientException)
            {
                Console.Out.WriteLine(lyncClientException);
                return;
            }
            catch (SystemException systemException)
            {
                if (IsLyncException(systemException))
                {
                    // Log the exception thrown by the Lync Model API.
                    Console.WriteLine("Error: " + systemException);
                    return;
                }
                else
                {
                    // Rethrow the SystemException which did not come from the Lync Model API.
                    throw;
                }
            }

            DoubleAnimation dbAnim = new DoubleAnimation();
            dbAnim.From = 0.0;
            dbAnim.To = 0.5;
            dbAnim.Duration = new Duration(TimeSpan.FromMilliseconds(2000));
            Storyboard.SetTargetName(dbAnim, statusCircle.Name);
            Storyboard.SetTargetProperty(dbAnim, new PropertyPath(Ellipse.OpacityProperty));

            mySb.Children.Add(dbAnim);

            _serialPort = new SerialPort();
            _serialPort.PortName = "COM5";
            _serialPort.BaudRate = 9600;
            _serialPort.WriteTimeout = 500;

            if (_serialPort.IsOpen)
            {
                _serialPort.Close();
            }

            _serialPort.Open();

            //
            // Making the assumption for now that the user is already signed in
            //

            _lyncClient.Self.Contact.ContactInformationChanged += new EventHandler<ContactInformationChangedEventArgs>(LyncStatusChanged);

            LyncStatusChanged(null, null);
        }

        private void OnUnloaded(object sender, RoutedEventArgs e)
        {
            this.Loaded -= new RoutedEventHandler(OnLoaded);

            if (_serialPort.IsOpen)
            {
                _serialPort.Close();
            }
        }


        private void LyncStatusChanged(object sender, ContactInformationChangedEventArgs e)
        {
            Color color;
            ContactAvailability currentAvailability = 0;

            try
            {
                currentAvailability = (ContactAvailability)_lyncClient.Self.Contact.GetContactInformation(ContactInformationType.Availability);
            }
            catch (LyncClientException ex)
            {
                Console.WriteLine(ex);
            }
            catch (SystemException systemException)
            {
                if (IsLyncException(systemException))
                {
                    // Log the exception thrown by the Lync Model API.
                    Console.WriteLine("Error: " + systemException);
                }
                else
                {
                    // Rethrow the SystemException which did not come from the Lync Model API.
                    throw;
                }
            }

            if (_availabilityColor.ContainsKey(currentAvailability))
            {
                color = _availabilityColor[currentAvailability];
            }
            else
            {
                color = Colors.LightSkyBlue;
            }

            this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new ColorStatusDelegate(InvokeColorStatusDelegate), color);

            this.Dispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                new GenericDelegate(
                    delegate
                    {
                        myStatus.Text = currentAvailability.ToString();
                        System.IO.Stream stream = this.GetType().Assembly.GetManifestResourceStream("Yaam.munny_" + color.ToString().Substring(3) + ".ico");
                        _icon = new IconBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default);
                        this.Icon = _icon.Frames[0];
                    }
            ));

        }


        /// <summary>
        /// Identify if a particular SystemException is one of the exceptions which may be thrown
        /// by the Lync Model API.
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        private bool IsLyncException(SystemException ex)
        {
            return
                ex is NotImplementedException ||
                ex is ArgumentException ||
                ex is NullReferenceException ||
                ex is NotSupportedException ||
                ex is ArgumentOutOfRangeException ||
                ex is IndexOutOfRangeException ||
                ex is InvalidOperationException ||
                ex is TypeLoadException ||
                ex is TypeInitializationException ||
                ex is InvalidComObjectException ||
                ex is InvalidCastException;
        }

    }

}
